package com.chlitina.store.modules.cash.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import org.hibernate.FetchMode;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.chlitina.store.common.mapper.JsonMapper;
import com.chlitina.store.common.model.ProductPaymentType;
import com.chlitina.store.common.persistence.Page;
import com.chlitina.store.common.service.BaseService;
import com.chlitina.store.common.utils.DateUtils;
import com.chlitina.store.common.utils.StoreCareUtils;
import com.chlitina.store.common.utils.StoreProductUtils;
import com.chlitina.store.modules.base.dao.StoreCareDao;
import com.chlitina.store.modules.base.dao.StoreProductDao;
import com.chlitina.store.modules.base.entity.Customer;
import com.chlitina.store.modules.base.entity.StoreCare;
import com.chlitina.store.modules.base.entity.StoreProduct;
import com.chlitina.store.modules.cash.dao.StoreCardTimesDao;
import com.chlitina.store.modules.cash.dao.StoreConsumeBuyProductDao;
import com.chlitina.store.modules.cash.dao.StoreConsumeDao;
import com.chlitina.store.modules.cash.dao.StoreConsumeDebtDao;
import com.chlitina.store.modules.cash.dao.StoreConsumeDoCareDao;
import com.chlitina.store.modules.cash.dao.StoreConsumeNewCardDao;
import com.chlitina.store.modules.cash.dao.StoreConsumeRechargeCardDao;
import com.chlitina.store.modules.cash.entity.StoreCardAll;
import com.chlitina.store.modules.cash.entity.StoreCardDuration;
import com.chlitina.store.modules.cash.entity.StoreCardProduct;
import com.chlitina.store.modules.cash.entity.StoreCardTimes;
import com.chlitina.store.modules.cash.entity.StoreConsume;
import com.chlitina.store.modules.cash.entity.StoreConsumeBuyProduct;
import com.chlitina.store.modules.cash.entity.StoreConsumeDebt;
import com.chlitina.store.modules.cash.entity.StoreConsumeDoCare;
import com.chlitina.store.modules.cash.entity.StoreConsumeNewCard;
import com.chlitina.store.modules.cash.entity.StoreConsumeRechargeCard;
import com.chlitina.store.modules.cash.model.CarePayment;
import com.chlitina.store.modules.cash.model.Consume;
import com.chlitina.store.modules.cash.model.ConsumeHistory;
import com.chlitina.store.modules.cash.model.PayElement;
import com.chlitina.store.modules.cash.model.ProductPayment;
import com.chlitina.store.modules.cash.model.ShoppingCart;
import com.chlitina.store.modules.cash.model.StoreCareCart;
import com.chlitina.store.modules.cash.model.StoreProductCart;
import com.chlitina.store.modules.employee.entity.Employee;
import com.chlitina.store.modules.sys.entity.Store;
import com.chlitina.store.modules.sys.utils.StoreUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * 散客消费Service
 * 
 * @author Songsong.xu
 * @version 2013-11-07
 */
@Component
@Transactional(readOnly = true)
public class StoreConsumeService extends BaseService {

	@SuppressWarnings("unused")
	private static Logger logger = LoggerFactory
			.getLogger(StoreConsumeService.class);

	@Autowired
	private StoreConsumeBuyProductDao storeConsumeBuyProductDao;
	@Autowired
	private StoreConsumeDao storeConsumeDao;
	@Autowired
	private StoreProductDao storeProductDao;
	@Autowired
	private StoreCareDao storeCareDao;
	@Autowired
	private StoreConsumeDoCareDao storeConsumeDoCareDao;
	@Autowired
	private StoreCardTimesDao storeCardTimesDao;
	@Autowired
	private StoreConsumeDebtDao storeConsumeDebtDao;
	@Autowired
	private StoreConsumeNewCardDao storeConsumeNewCardDao;
	@Autowired
	private StoreConsumeRechargeCardDao storeConsumeRechargeCardDao;
	@Autowired
	private MemberService memberService;

	public boolean ifDebtByConsume(Long consumeId) {
		DetachedCriteria dc = storeConsumeDebtDao.createDetachedCriteria();
		dc.add(Restrictions.eq("debtConsume.id", consumeId));
		List<StoreConsumeDebt> find = storeConsumeDebtDao.find(dc);
		if(find.size() == 0) {
			return false;
		} else {
			if(find.get(0).getRefundConsume() == null) {
				return true;
			} else {
				return false;	
			}
		}
	}
	
	public StoreConsume commitNonmember(ShoppingCart shoppingCart) {
		PayElement payElement = shoppingCart.getPayElement();
		StoreConsume storeConsume = shoppingCart.getStoreConsume();
		storeConsume.setConsume_Type(StoreConsume.TYPE_CONSUME_NOMEMBER);
		storeConsume.setMoney_To_Pay(payElement.getRealPrice());
		storeConsume.setCustomer(null);
		storeConsumeDao.save(storeConsume);

		List<StoreProductCart> success = new ArrayList<StoreProductCart>();
		List<StoreProductCart> failed = new ArrayList<StoreProductCart>();
		// products
		List<StoreProductCart> products = shoppingCart.getProducts();

		for (StoreProductCart cart : products) {
			StoreConsumeBuyProduct buyProduct = createStoreConsumeBuyProduct(
					cart, cart.getEmployee());
			if (buyProduct == null) {
				failed.add(cart);
			} else {
				buyProduct.setStoreConsume(storeConsume);
				storeProductDao.save(cart.getStoreProduct());
				storeProductDao.flush();
				storeConsumeBuyProductDao.save(buyProduct);
				success.add(cart);
			}
		}

		// cares
		List<StoreCareCart> cares = shoppingCart.getCares();
		for (StoreCareCart cart : cares) {
			StoreConsumeDoCare storeConsumeDoCare = createStoreConsumeDoCare(cart);
			storeConsumeDoCare.setStoreConsume(storeConsume);
			storeConsumeDoCareDao.save(storeConsumeDoCare);
		}
		System.err.println("DEBUG---Failed to buy products:");
		for (StoreProductCart cart : failed) {
			System.err.println("Error--\t" + "real amount :"
					+ cart.getStoreProduct().getAmount()
					+ "\t but required is :" + cart.getCount());
		}

		return storeConsume;
	}

	private StoreConsumeBuyProduct createStoreConsumeBuyProduct(
			StoreProductCart cart, Employee employee) {
		StoreProduct storeProduct = cart.getStoreProduct();
		storeProductDao.refresh(storeProduct);
		int left = storeProduct.getAmount() - cart.getCount();
		if (left < 0) {
			return null;
		}
		storeProduct.setAmount(left);
		StoreConsumeBuyProduct s = new StoreConsumeBuyProduct();
		s.setAmount(cart.getCount());
		s.setEmployee(employee);
		s.setPayment_type(ProductPaymentType.CASH);
		s.setStoreProduct(storeProduct);
		s.setRetail_price(StoreProductUtils.getRetailPrice(storeProduct));
		// can not be changed, current use Retail_price as real cost
		s.setReal_cost(s.getRetail_price());
		s.setReal_cost_total(s.getRetail_price() * s.getAmount());
		cart.setRealCost(s.getRetail_price());
		cart.setRealCostTotal(s.getReal_cost_total());
		
		ProductPayment payment = new ProductPayment();
		cart.setPayment(payment);
	
		payment.setCash(s.getReal_cost_total());
		s.setBonus(memberService.getBonus(StoreUtils.getStore(), cart));
		return s;
	}

	private StoreConsumeDoCare createStoreConsumeDoCare(StoreCareCart cart) {
		StoreConsumeDoCare s = new StoreConsumeDoCare();
		s.setEmployee(cart.getEmployee());
		s.setPayment_type(ProductPaymentType.CASH);
		StoreCare storeCare = cart.getStoreCare();
		s.setStoreCare(storeCare);
		cart.setRealCostTotal(cart.getSinglePrice()*cart.getCount());
		s.setRetail_price(cart.getSinglePrice());
		s.setReal_cost(cart.getRealCostTotal());
		CarePayment payment = new CarePayment();
		cart.setPayment(payment);
		payment.setCash(cart.getRealCostTotal());
		
		s.setBonus(memberService.getBonus(StoreUtils.getStore(), cart));
		return s;
	}

	public List<StoreConsume> findByCustomer(Long customerId) {
		DetachedCriteria dc = storeConsumeDao.createDetachedCriteria();
		Store store = StoreUtils.getStore();
		dc.add(Restrictions.eq("store.id", store.getId()));
		if (customerId != null) {
			dc.add(Restrictions.eq("customer.id", customerId));
		}
		dc.addOrder(Order.desc("consume_Time"));

		return storeConsumeDao.find(dc);
	}

	public List<Customer> findByParameter(String startDate, String endDate,
			String type, int number) {
		Date start = null;
		Date end = null;
		try {
			start = DateUtils.parseDate(startDate);
			start = DateUtils.getBeginOfDay(start);
		} catch (Exception e) {
			// System.out.println("字符串转换日期异常");
		}
		try {
			end = DateUtils.parseDate(endDate);
			end = DateUtils.getEndOfDay(end);
		} catch (Exception e) {
			// System.out.println("字符串转换日期异常");
		}
		List<Customer> customerList = Lists.newArrayList();
		List<Customer> storeCustomers = StoreUtils.getStoreCustomers();
		for (Customer customer : storeCustomers) {
			if ("times".equals(type)) {
				int times = findTimesByCustomer(customer.getId(), start, end);
				if (times <= number) {
					customerList.add(customer);
				}
			} else if ("money".equals(type)) {
				float money = findMoneyByCustomer(customer.getId(), start, end);
				if (money <= number) {
					customerList.add(customer);
				}
			}

		}
		return customerList;
	}

	public List<StoreConsume> findConsumeByDuration(Date start, Date end) {
		storeConsumeDao.clear();
		DetachedCriteria dc = storeConsumeDao.createDetachedCriteria();
		Store store = StoreUtils.getStore();
		dc.add(Restrictions.eq("store.id", store.getId()));
		if (start != null && end == null) {
			dc.add(Restrictions.ge("consume_Time", start));
		} else if (start == null && end != null) {
			dc.add(Restrictions.le("consume_Time", end));
		} else if (start != null && end != null) {
			dc.add(Restrictions.between("consume_Time", start, end));
		}
		dc.add(Restrictions.eq("confirmed", StoreConsume.TYPE_CONFIRMED));
		List<StoreConsume> list = storeConsumeDao.find(dc);
		return list;
	}

	public int findTimesByCustomer(Long id, Date start, Date end) {
		List<StoreConsume> list = findConsumeByDuration(start, end);
		int times = 0;
		for (StoreConsume storeConsume : list) {
			if (storeConsume.getCustomer() != null
					&& id.equals(storeConsume.getCustomer().getId())) {
				times++;
			}
		}
		return times;
	}

	public float findMoneyByCustomer(Long id, Date start, Date end) {
		List<StoreConsume> list = findConsumeByDuration(start, end);
		float money = 0;
		for (StoreConsume storeConsume : list) {
			if (storeConsume.getCustomer() != null
					&& id.equals(storeConsume.getCustomer().getId())) {
				Long consumeId = storeConsume.getId();
				List<StoreConsumeBuyProduct> buyProducts = storeConsumeBuyProductDao
						.findListByConsume(consumeId);
				for (StoreConsumeBuyProduct storeConsumeBuyProduct : buyProducts) {
					money += storeConsumeBuyProduct.getReal_cost();
				}
				List<StoreConsumeDoCare> doCares = storeConsumeDoCareDao
						.findListByConsume(consumeId);
				for (StoreConsumeDoCare storeConsumeDoCare : doCares) {
					money += storeConsumeDoCare.getReal_cost();
				}
				List<StoreConsumeNewCard> newCards = storeConsumeNewCardDao
						.findListByConsume(consumeId);
				for (StoreConsumeNewCard storeConsumeNewCard : newCards) {
					money += storeConsumeNewCard.getReal_cost();
				}
				List<StoreConsumeRechargeCard> rechargeCards = storeConsumeRechargeCardDao
						.findListByConsume(consumeId);
				for (StoreConsumeRechargeCard storeConsumeRechargeCard : rechargeCards) {
					money += storeConsumeRechargeCard.getReal_cost();
				}
			}
		}
		return money;
	}

	public Page<StoreConsume> findNomemberConsumes(String startDate,
			String endDate, Page<StoreConsume> page) {
		DetachedCriteria dc = storeConsumeDao.createDetachedCriteria();
		Store store = StoreUtils.getStore();
		dc.add(Restrictions.eq("store.id", store.getId()));
		Date start = null;
		Date end = null;
		start = DateUtils.parseDate(startDate);
		if (start != null) {
			start = DateUtils.getBeginOfDay(start);
		}
		end = DateUtils.parseDate(endDate);
		if (end != null) {
			end = DateUtils.getEndOfDay(end);
		}

		if (start != null && end == null) {
			dc.add(Restrictions.ge("consume_Time", start));
		} else if (start == null && end != null) {
			dc.add(Restrictions.le("consume_Time", end));
		} else if (start != null && end != null) {
			dc.add(Restrictions.between("consume_Time", start, end));
		}
		dc.add(Restrictions.isNull("customer"));
		dc.addOrder(Order.desc("id"));
		Page<StoreConsume> find = storeConsumeDao.find(page, dc);
		 
		return find;
	}

	public void findNomemberConsumes(ConsumeHistory history, Page<StoreConsume> page) {
		 Page<StoreConsume>  storeConsumes = findNomemberConsumes(
				history.getStartDate(), history.getEndDate(),page);
		 Page<Consume> realPage = history.getPage();
		realPage.getList().clear();
		for (StoreConsume storeConsume : storeConsumes.getList()) {
			Consume consume = new Consume();
			consume.setStoreConsume(storeConsume);
			consume.setProducts(storeConsumeBuyProductDao
					.findListByConsume(storeConsume.getId()));
			consume.setCares(storeConsumeDoCareDao
					.findListByConsume(storeConsume.getId()));
			realPage.getList().add(consume);
			consume.updateDecriptions();
		}
		realPage.setCount(storeConsumes.getCount());
		realPage.setPageSize(storeConsumes.getPageSize());
		realPage.setPageNo(storeConsumes.getPageNo());
	}

	private String findTimesCardConsumes(long cID) {
		HashMap<String, Object> result = Maps.newHashMap();
		HashMap<String, Object> cardDetails = Maps.newHashMap();
		StoreCardTimes card = storeCardTimesDao.findOne(cID);
		if (card != null) {
			cardDetails.put("id", card.getId());
			cardDetails.put("number", card.getCardNumber());
			cardDetails.put("name", card.getTemplate().getName());
			cardDetails.put("beginTime", DateUtils.formatDate(
					card.getBeginTime(), "yyyy-MM-dd HH:mm:ss"));
			cardDetails.put("remainTimes", card.getRemainTimes());
		}
		result.put("card", cardDetails);
		DetachedCriteria dc = storeConsumeDoCareDao.createDetachedCriteria();
		dc.setFetchMode("storeConsume", FetchMode.JOIN);
		dc.createAlias("storeConsume", "sc");
		dc.addOrder(Order.desc("sc.consume_Time"));

		List<StoreConsumeDoCare> find = storeConsumeDoCareDao.find(dc);
		List<HashMap<String, Object>> consumes = Lists.newArrayList();
		HashMap<String, Object> consume;
		for (StoreConsumeDoCare doCare : find) {
			consume = Maps.newHashMap();
			consume.put("type", "做护理");
			consume.put("employeeName", doCare.getEmployee().getName());
			consume.put("retailPrice", doCare.getRetail_price());
			consume.put("realCost", doCare.getReal_cost());
			consume.put("remark",
					doCare.getRemark() == null ? "" : doCare.getRemark());
			consume.put("careName",
					StoreCareUtils.getName(doCare.getStoreCare()));
			consume.put("careType",
					StoreCareUtils.getTypeString(doCare.getStoreCare()));
			consume.put("time", DateUtils.formatDate(doCare.getStoreConsume()
					.getConsume_Time(), "yyyy-MM-dd HH:mm:ss"));
			consumes.add(consume);
		}
		result.put("consumes", consumes);
		return JsonMapper.getInstance().toJson(result);
	}

	public String findConsumeByCard(Class clazz, long cID) {
		if (clazz.equals(StoreCardTimes.class)) {
			return findTimesCardConsumes(cID);
		} else if (clazz.equals(StoreCardDuration.class)) {
			DetachedCriteria dc = storeConsumeDoCareDao
					.createDetachedCriteria();
			dc.add(Restrictions.and(Restrictions.eq("payment_type",
					StoreConsumeDoCare.PAY_TYPE_DURATION_CARD), Restrictions
					.eq("card_id", cID)));
			List<StoreConsumeDoCare> find = storeConsumeDoCareDao.find(dc);
			System.err.println(find);
		} else if (clazz.equals(StoreCardProduct.class)) {
			DetachedCriteria dc = storeConsumeBuyProductDao
					.createDetachedCriteria();
			dc.add(Restrictions.and(Restrictions.eq("payment_type",
					StoreConsumeBuyProduct.PAY_TYPE_CARD_PRODUCT), Restrictions
					.eq("card_id", cID)));
			List<StoreConsumeBuyProduct> find = storeConsumeBuyProductDao
					.find(dc);
			System.err.println(find);

		} else if (clazz.equals(StoreCardAll.class)) {
			HashMap<Object, Object> object = Maps.newHashMap();
			DetachedCriteria dc = storeConsumeDoCareDao
					.createDetachedCriteria();
			dc.add(Restrictions.and(Restrictions.eq("payment_type",
					StoreConsumeDoCare.PAY_TYPE_All_CARD), Restrictions.eq(
					"card_id", cID)));
			List<StoreConsumeDoCare> find1 = storeConsumeDoCareDao.find(dc);
			for (StoreConsumeDoCare doCare : find1) {

			}

			dc = storeConsumeBuyProductDao.createDetachedCriteria();
			dc.add(Restrictions.and(Restrictions.eq("payment_type",
					StoreConsumeBuyProduct.PAY_TYPE_CARD_ALL), Restrictions.eq(
					"card_id", cID)));
			List<StoreConsumeBuyProduct> find2 = storeConsumeBuyProductDao
					.find(dc);
			System.err.println(find2);

		}
		return "";
	}

	public String findNomemberConsumeDetail(long consumeID) {
		HashMap<Object, Object> result = Maps.newHashMap();
		StoreConsume consume = storeConsumeDao.findOne(consumeID);
		if (consume != null) {
			result.put("number", consume.getConsume_Number());
			result.put("time", DateUtils.formatDate(consume.getConsume_Time(),
					"yyyy-MM-dd HH:mm:ss"));
			result.put("money", consume.getMoney_To_Pay());
			List<HashMap<Object, Object>> products = Lists.newArrayList();
			List<StoreConsumeBuyProduct> buyProducts = storeConsumeBuyProductDao
					.findListByConsume(consume.getId());
			HashMap<Object, Object> temp = null;
			StoreProduct storeProduct = null;
			for (StoreConsumeBuyProduct p : buyProducts) {
				temp = Maps.newHashMap();
				storeProduct = p.getStoreProduct();
				temp.put("name", StoreProductUtils.getName(storeProduct));
				temp.put("amount", p.getAmount());
				temp.put("retailPrice", p.getRetail_price());
				temp.put("spec", StoreProductUtils.getSpec(storeProduct));
				temp.put("employee", p.getEmployee().getName());
				temp.put("realCost", p.getReal_cost());
				temp.put("realCostTotal", p.getReal_cost_total());
				products.add(temp);
			}
			result.put("products", products);
			List<HashMap<Object, Object>> cares = Lists.newArrayList();
			List<StoreConsumeDoCare> doCares = storeConsumeDoCareDao
					.findListByConsume(consume.getId().longValue());
			StoreCare storeCare = null;
			for (StoreConsumeDoCare c : doCares) {
				temp = Maps.newHashMap();
				storeCare = c.getStoreCare();
				temp.put("name", StoreCareUtils.getName(storeCare));
				temp.put("retailPrice", c.getRetail_price());
				temp.put("duration", StoreCareUtils.getDuration(storeCare));
				temp.put("employee", c.getEmployee().getName());
				temp.put("realCost", c.getReal_cost());
				cares.add(temp);
			}
			result.put("cares", cares);
			String json = JsonMapper.getInstance().toJson(result);
			System.err.println(json);
			return json;
		}
		return JsonMapper.getInstance().toJson(result);
	}
}
